var folderplus = {

	/**
	 * Go to folder of current message Always use the first tab of type "Folder"
	 * Don't use a tab as gloda search Change folder even if message already
	 * visible in a virtual folder.
	 */
	goToFolder : function() {
		var aMsgHdr = gFolderDisplay.selectedMessage;
		var folder = gFolderTreeView.getFolderForMsgHdr(aMsgHdr);
		this._GoToFolder(folder, aMsgHdr);
	},

	/**
	 * Same as goToFolder. Maybe it will be changed in future :-/
	 */
	goToThread : function() {
		folderplus.log("folderDisplay.goToThread");
		var aMsgHdr = gFolderDisplay.selectedMessage;
		function callback(aFolderURI){
			folderplus.log("goToThread.callback" + aFolderURI);
			var folder = GetMsgFolderFromUri(aFolderURI);
			folderplus._GoToFolder(folder, aMsgHdr);
		};
		folderplus._getFolder(aMsgHdr, callback);
	},
	
	goToLastMoved : function () {
		folderplus.log("folderDisplay.goToLastMoved");
		var folderId = pref.getCharPref("mail.last_msg_movecopy_target_uri");
		var folder = GetMsgFolderFromUri(folderId, false)
		this._GoToFolder(folder, null);
	},

	goToInbox : function () {
		folderplus.log("folderDisplay.goToInbox");
		var defaultAccountRootFolder = GetDefaultAccountRootFolder();

		// if no default account, get msg isn't going do anything anyways
		// so bail out
		if (!defaultAccountRootFolder)
			return;
		
		var inboxFolder = defaultAccountRootFolder.getFolderWithFlags(Components.interfaces.nsMsgFolderFlags.Inbox);
		
		if (!inboxFolder)
			return;
		
		this._GoToFolder(inboxFolder, null);
	},

	/**
	 * Go to folder of current message Always use the first tab of type "Folder"
	 * Don't use a tab as gloda search Change folder even if message already
	 * visible in a virtual folder.
	 */
	_GoToFolder : function(folder, aMessage) {
		folderplus.log("folderDisplay._GoToFolder");
		folderplus.log("folder = " + folder);
		// inspired from MsgDisplayMessageInFolderTab
		// but force using first tab to manage virdual folder.
		var tabmail = document.getElementById("tabmail");
		var folderTabs = tabmail.tabModes["folder"];
		var folderTab = folderTabs.tabs[0];
		var folderDisplay = folderTab.folderDisplay;
		folderDisplay.selectMessageComingUp();
		tabmail.switchToTab(folderTab);
		if (folderDisplay.displayedFolder != folder
				|| folderDisplay.view.isVirtual) {
			// Force select the folder
			folderDisplay.show(folder);
			gFolderTreeView.selectFolder(folder, true);
		}
		// Force select the message
		if (aMessage) {
			folderplus.log("call folderDisplay.selectMessage" + aMessage.messageId);
			var folderDB = GetMsgFolderFromUri(folder.URI, false); 
			var message = folderDB.msgDatabase.getMsgHdrForMessageID(aMessage.messageId);
			folderDisplay.selectMessage(message);	
		}
	},

	/**
	 * 
	 */
	moveToThreadFolder : function() {
		var aMsgHdr = gFolderDisplay.selectedMessage;
		//folderplus.log("moveToThreadFolder messageKey = " + aMsgHdr.messageKey);
		function callback(aFolderURI){
			/* Comme l'exec du callback est asynchrone (du lancement de la commande)
			 * si utilisation du bouton droit, le message qui a reçu le bouton droit
			 * peut être different du message courant quand le callback est executé.
			 */
			var aMsgHdr = gFolderDisplay.selectedMessage;
			//folderplus.log("moveToThreadFolder.callback" + aFolderURI + "," + aMsgHdr.messageKey);
			var folder = GetMsgFolderFromUri(aFolderURI);
			// use MsgMoveMessage to set "move again"
			MsgMoveMessage(folder);
		};
		folderplus._getFolder(aMsgHdr, callback);
	},
	
	/**
  	 * 
	 */
	moveAndGoToThreadFolder : function() {
		/** Bug : le message n'est pas sélectionné après le déplacement. Peut-être car l'appel
		 * selectMessage est fait trop tôt.
		 */
		//folderplus.log("folderDisplay.moveAndGoToThreadFolder");
		var aMsgHdr = gFolderDisplay.selectedMessage;
		folderplus.log("message=" + aMsgHdr.messageId);
		function callback(aFolderURI){
			//folderplus.log("moveToThreadFolder.callback to " + aFolderURI);
			var folder = GetMsgFolderFromUri(aFolderURI);
			// use MsgMoveMessage to set "move again"
			MsgMoveMessage(folder);
			folderplus._GoToFolder(folder, aMsgHdr);
		};
		folderplus._getFolder(aMsgHdr, callback);
	},
	
	/**
	 * set move to conversation and go to folder menu in mailContext menu
	 * and go menu.
	 * Async method :-/ First call a globa search to get a gloda message from 
	 * aMsgHdr then call a second gloda search to find all messages from the 
	 * conversation.
	 */
	fillMailContextMenu : function(event) {
		folderplus.log("fillMailContextMenu");
		var aMsgHdr = gFolderDisplay.selectedMessage;
		if (aMsgHdr == null) {
			return;
		}
		// Update lables menus which talk about Folder (and not conversation)
		var folderName = aMsgHdr.folder.prettyName;
		folderplus._updateAllFolderMenus(folderName);

		function callback(aFolderURI){
			//folderplus.log("fillMailContextMenu.callback");
			var label = "";
			if (aFolderURI == "error") {
				//folderplus.log("fillMailContextMenu.callback:erreur");
				label = "?";
			} else {
				// get the msg folder we're moving messages into
				var destMsgFolder = GetMsgFolderFromUri(aFolderURI);
				var folderName = destMsgFolder.prettyName;
				label = folderName;
			}
			folderplus._updateAllCoversationMenus(label);
		};
		this._getFolder(aMsgHdr, callback);
	},
	
	/**
	 * set move to conversation and go to folder menu in mailContext menu
	 * and go menu.
	 * Async method :-/ First call a globa search to get a gloda message from 
	 * aMsgHdr then call a second gloda search to find all messages from the 
	 * conversation.
	 * @return nothing, use the callback(String = folderURI).
	 */
	_getFolder : function(aMsgHdr, callback) {
		// cf msgHdrViewOverlay.js/ConversationOpener
		/* First, look for a gloda message from messege header. */
		var aListener = {
			onItemsAdded : function myListener_onItemsAdded(aItems, aCollection) {},
			onItemsModified : function myListener_onItemsModified(aItems,aCollection) {},
			onItemsRemoved : function myListener_onItemsRemoved(aItems,aCollection) {},
			onQueryCompleted : function myListener_onQueryCompleted(aCollection) {
				try {
					if (!aCollection.items.length) {
						throw "Couldn't find a glodaMessage for msg: " + aMsgHdr.messageId;
					} else {
						folderplus.log("GlodaMessage founded, look for conversation for message \"" + aMsgHdr.mime2DecodedSubject + "\"");
						var aGlodaMessage = aCollection.items[0];
						var aConvCollection = aGlodaMessage.conversation
								.getMessagesCollection(aConvListener);
					}
				} catch (e) {
					Components.utils.reportError(e);
					callback("error");
				}
			}
		};
		/* Second, look for a conversation from gloda message. */
		var aConvListener = {
			onItemsAdded : function myListener_onItemsAdded(aItems, aCollection) {},
			onItemsModified : function myListener_onItemsModified(aItems,aCollection) {},
			onItemsRemoved : function myListener_onItemsRemoved(aItems,aCollection) {},
			onQueryCompleted : function myListener_onQueryCompleted(aCollectionConv) {
				try {
					if (!aCollectionConv.items.length) {
						throw "Couldn't find conversation message for msg: " + aMsgHdr.messageId;
					}
					var msgConvCount = aCollectionConv.items.length;
					//var isNewer = folderplus._isNewerConv(aCollectionConv.items, aMsgHdr.date);
					var isAllConvInFolder = folderplus._isAllConvInFolder(aCollectionConv.items, aMsgHdr.folder.URI)
					if (msgConvCount == 1 || isAllConvInFolder) {
						// si le message ne fait parti d'aucune conversation
						// ou qu'il est le 1° d'une conversation qui commence
						// try to found folder by subject
						query=Gloda.newQuery(Gloda.NOUN_MESSAGE);
						var aSubject = aMsgHdr.mime2DecodedSubject;
						//aSubject = folderplus._getPrefixeRemoveREFWD(aSubject);
						// no need to remove 're:'.
						aSubject = "\"" + aSubject + "\"";
						/** gloda search on subject is case-insensitive, accent-insensitive, not exact,
						 * seems to automaticaly remove re:, tr:, use operator as '-' so need double quotes.
						 * [Migration SVN] : compte-rendu de réunion => 0 result
						 * \"[Migration SVN] : compte-rendu de réunion\" => 6 results
						 * \"RE: [Migration SVN] : compte-rendu de réunion\" => 6 results
						 */
						folderplus.log("founded by conversation but only message or all in current folder, look for subject : " +aSubject);
						query.subjectMatches(aSubject);
						query.getCollection(aSubjectListenerExact);
					} else {
						folderplus.log("founded by conversation, look for rigth folder in conversation");
						var folderURI = folderplus._findConversationFolder(aCollectionConv.items, aMsgHdr);
						callback(folderURI);
					}    					
				} catch (e) {
					Components.utils.reportError(e);
					callback("error");
				}
			}
		};
		/* Third, look for message by subject. */
		var aSubjectListenerExact = {
			onItemsAdded : function myListener_onItemsAdded(aItems, aCollection) {},
			onItemsModified : function myListener_onItemsModified(aItems,aCollection) {},
			onItemsRemoved : function myListener_onItemsRemoved(aItems,aCollection) {},
			onQueryCompleted : function myListener_onQueryCompleted(aCollection) {
				try {
					if (!aCollection.items.length) {
						throw "Couldn't find message by subject";
					}
					var msgConvCount = aCollection.items.length;
					//var isNewer = folderplus._isNewerConv(aCollection.items, aMsgHdr.date);
					var isAllConvInFolder = folderplus._isAllConvInFolder(aCollection.items, aMsgHdr.folder.URI)
					if (msgConvCount == 1 || isAllConvInFolder) {
						// try to found folder by prefix
						var aSubject = aMsgHdr.mime2DecodedSubject;
						var re = /(.*)\s*:/g;
						var result = re.exec(aSubject);
						if (result) {
							folderplus.log("only one message by subject founded or all in current folder then search by prefix, look for prefix.");
							aSubject = "\"" + result[1] + "\"";
							folderplus.log("Prefix used : " + aSubject);
							query=Gloda.newQuery(Gloda.NOUN_MESSAGE);
							query.subjectMatches(aSubject);
							query.getCollection(aPrefixListener);
						} else {
							folderplus.log("only one message found  by subject and can not search by prefix.");
							callback("error");
						}
					} else {
						folderplus.log("found by subject (more than message : " + aCollection.items.length + "), look for right folder.");						
						var folderURI = folderplus._findConversationFolder(aCollection.items, aMsgHdr);
						callback(folderURI);				
					}
				} catch (e) {
					Components.utils.reportError(e);
					callback("error");
				}
			}
		};
		/* fourth , look for message by prefix. */
		var aPrefixListener = {
			onItemsAdded : function myListener_onItemsAdded(aItems, aCollection) {},
			onItemsModified : function myListener_onItemsModified(aItems,aCollection) {},
			onItemsRemoved : function myListener_onItemsRemoved(aItems,aCollection) {},
			onQueryCompleted : function myListener_onQueryCompleted(aCollection) {
				try {
					if (!aCollection.items.length) {
						throw "Couldn't find message by prefix";
					} else {
						folderplus.log("Found conversation by prefix, look for the right folder.");
						var folderURI = folderplus._findConversationFolder(aCollection.items, aMsgHdr);
						callback(folderURI);				
					}
				} catch (e) {
					Components.utils.reportError(e);
					callback("error");
				}
			}
		};
		/* First, look for a gloda message from messege header. */
		if (aMsgHdr == null) {
			callback("error");
		} else {
			var aCollection = Gloda.getMessageCollectionForHeader(aMsgHdr, aListener);
		}
	},
	
	/**
	 * test if all messages inside the conv are newer that a specified date.
	 * @returns boolean, true if conv is empty
	 */
	_isNewerConv : function (aConvItems, aMessageDate) {
		for (var i = 0; i < aConvItems.length; i++) {
			var aDate = aConvItems[i].folderMessage.date;
			if (aDate < aMessageDate) {
				return false;
			}
		}
		return true;
	},

	/**
	 * @param String folderURI
	 * @returns boolean, true if conv is empty
	 */
	_isAllConvInFolder : function (aConvItems, aFolderURIComp) {
		for (var i = 0; i < aConvItems.length; i++) {
			if (aConvItems[i].folderMessage) {
				var aFolderURI = aConvItems[i].folderMessage.folder.URI;
				if (aFolderURI != aFolderURIComp) {
					return false;
				}
			} else {
				folderplus.log("can't get folderMessage of gloda message" + aItem.subject + ", " + aItem.id);
				// Pas de throw pour tenter de passage au message suivant.
			}
		}
		return true;
	},

	/**
	 * This function decides which Folder use as the "conversation folder".
	 * Look for the last message received in the conversation. Don't use the messages
	 * inside the current folder to avoid to return the 'inbox'.
	 * @params messages collection
	 * @return String folderURI ou "error"
	 */
	_findConversationFolder : function (items, aMessage) {
		var firstDate = null;
		var firstMessage = null;
		/*
		for ( var i = 0; i < items.length; i++) {
			var aItem = items[i];
			var aDate = aItem.folderMessage.date;
			var aFolder = aItem.folderMessage.folder.URI;
			if (firstDate == null || (aDate < firstDate)) {
				firstDate    = aItem.folderMessage.date;
				firstMessage = aItem; 
			}
		}
		*/
		var aMessageDate = aMessage.date;
		var aMessageFolder = aMessage.folder.URI;
		for ( var i = 0; i < items.length; i++) {
			var aItem = items[i];
			if (aItem.folderMessage) {
				//folderplus.log(aItem.subject + ", " + aItem.folderMessage.folder.URI + ", " + new Date(aItem.folderMessage.date/1000).toString());
				var aDate = aItem.folderMessage.date;
				var aFolder = aItem.folderMessage.folder.URI;
				if ((aDate < aMessageDate)
				    && (aFolder != aMessageFolder)
				    && (firstDate == null || (aDate > firstDate))) {
					firstDate    = aItem.folderMessage.date;
					firstMessage = aItem; 
				}
			} else {
				folderplus.log("can't get folderMessage of gloda message" + aItem.subject + ", " + aItem.id);
				// Pas de throw pour tenter de passage au message suivant.
			}
		}
		//folderplus.log("_findConversationFolder returns : " + firstMessage.folderURI);
		if (firstMessage) {
			return firstMessage.folderURI;
		} else {
			throw "Cound not find right folder";
			//return "error";
		}
	},
	
	_getPrefixeRemoveREFWD : function (aSubject) {
		var regex = /((re|fwd|tr) ?)+: ?/gi;
		var result = aSubject.replace(regex, "");
		return result;
	},
	
	_getPrefixe : function (aSubject) {
		var result = null;
		var aSubjectToPrefixe = folderplus._getPrefixeRemoveREFWD(aSubject);
		var regBracket = /\[(.+)\]/;		
		var resultBracket = regBracket.exec(aSubjectToPrefixe);
		if (resultBracket == null) {
			var regDot = /(.+) ?([:/]| -)/;
			var resultDot = regDot.exec(aSubjectToPrefixe);
			if (resultDot != null) {
				result = resultDot[1].trim();
			}
		} else {
			result = resultBracket[1].trim();
		}
		return result;
	},
	
	/**
	 * Update all Folder menu labels.
	 */
	_updateAllFolderMenus : function (folderName) {
		folderplus.log("_updateAllFolderMenus");
		var bundle = document.getElementById("folderplus-strings");
		var aLabel = bundle.getFormattedString("folderplus.GoToFolder",[ folderName ], 1);
		var aMenuItem = document.getElementById("folderplus.GoToFolder.mailContext");
		aMenuItem.label = aLabel;
		aMenuItem = document.getElementById("folderplus.GoToFolder.goMenu");
		aMenuItem.label = aLabel;
		document.getElementById("folderplus_appmenu_GoToFolder").label = aLabel;
	},
	
	/**
	 * Update all conversation menu labels with folder name.
	 */
	_updateAllCoversationMenus : function (folderName) {
		//folderplus.log("_updateAllCoversationMenus");
		var bundle = document.getElementById("folderplus-strings");
		var aLabel     = bundle.getFormattedString("folderplusMoveToConversation", [ folderName ], 1);
		var aLabelMenu = bundle.getFormattedString("folderplusMoveToConversationMenu", [ folderName ], 1);
		
		document.getElementById("folderplus.moveToConversationFolder").label = aLabelMenu;
		document.getElementById("folderplus.GoToConversation").label = aLabel;
		document.getElementById("folderplus.GoToConversation.goMenu").label = aLabel;
		document.getElementById("folderplus_appmenu_goToConversation").label = aLabel;
		document.getElementById("folderplus.moveToConversationFolder.messageMenu").label = aLabelMenu;
		document.getElementById("folderplus_appmenu_moveToConversationFolder").label = aLabelMenu;
	},
	
	_updateMessageHeader : function () {
		var aMsgHdr = gFolderDisplay.selectedMessage;
		function callback(aFolderURI){
			var isMovable = false;
			folderplus.log("update UI \(_updateMessageHeader.callback\)");
			if (aFolderURI == "error") {
				label = "?";
			} else {
				// get the msg folder we're moving messages into
				var destMsgFolder = GetMsgFolderFromUri(aFolderURI);
				var folderName = destMsgFolder.prettyName;
				label = folderName;
				/* if message is not in the conversation folder, show it to user */ 
				var aMsgHdr = gFolderDisplay.selectedMessage;
				var folder = gFolderTreeView.getFolderForMsgHdr(aMsgHdr);
				if (folder != destMsgFolder) {
					isMovable = true;
				}
			}
			document.getElementById("expandedconversationBox").value = label;
			document.getElementById("expandedconversationBox").tooltiptext = "tooltips";
			document.getElementById("expandedconversationicon").setAttribute("movable", isMovable ? "true" : "false");
			var compactHeaderHeader = document.getElementById("folderplusConversationHeaderCompactHeader");
			if (compactHeaderHeader) {
				compactHeaderHeader.value = label;
				document.getElementById("expandedconversationiconCompactHeader").setAttribute("movable", isMovable ? "true" : "false");
			}
		};
		folderplus._getFolder(aMsgHdr, callback);
	},

	init : function() {
		folderplus._console = Components.classes["@mozilla.org/consoleservice;1"].
			getService(Components.interfaces.nsIConsoleService);
		var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                    .getService(Components.interfaces.nsIPrefService);
		this._prefs = prefs.getBranch("extensions.folderplus.");
		
		folderplus.log("init");
		
		if (!this._prefs.getBoolPref("firstRunDone")) {
			this._prefs.setBoolPref("firstRunDone", true);
			this._installButton("header-view-toolbar", "folderplusHdrConversationButton");
		}

		gMessageListeners.push({
			onStartHeaders: function () {},
			onEndHeaders: function() {
				folderplus._updateMessageHeader();  
			},
			onEndAttachments: function () {},
		});
	},
	
	
	log : function (aMessage) {
		var logLevel = this._prefs.getCharPref("logLevel");
		if (logLevel == "debug") {
			this._console.logStringMessage("folderplus," + aMessage);
		}
	},
	
	_prefs : null,
	_console : null,	
	
	/**
	 * Installs the toolbar button with the given ID into the given
	 * toolbar, if it is not already present in the document.
	 *
	 * @param {string} toolbarId The ID of the toolbar to install to.
	 * @param {string} id The ID of the button to install.
	 * @param {string} afterId The ID of the element to insert after. @optional
	 */
	_installButton : function (toolbarId, id, afterId) {
	    if (!document.getElementById(id)) {
		var toolbar = document.getElementById(toolbarId);
	 
		// If no afterId is given, then append the item to the toolbar
		var before = null;
		if (afterId) {
		    var elem = document.getElementById(afterId);
		    if (elem && elem.parentNode == toolbar)
			before = elem.nextElementSibling;
		}
	 
		toolbar.insertItem(id, before);
		toolbar.setAttribute("currentset", toolbar.currentSet);
		document.persist(toolbar.id, "currentset");
	 
		if (toolbarId == "addon-bar")
		    toolbar.collapsed = false;
	    }
	}
};

// typeof test is usefull for xpcshell test unit
if (typeof(window)!='undefined') {
	window.addEventListener("load", function load(event){
		window.removeEventListener("load", load, false); //remove listener, no longer needed
		folderplus.init();
	}, false);
}